함수형 컴포넌트와 클래스형 컴포넌트

둘의 다른점?

2023-09-01

함수형 컴포넌트로도 ,클래스를 이용해서도 컴포넌트를 만들 수 있습니다. 둘의 차이가 궁금했습니다.

거북잉..

먼저 GTP한테 물어보았슴니다..

함수형vs클래스형

먼저 다음의 컴포넌트가 존재합니다.

function Profile(props){
    const showMessage = () => {
        alert(props.user);
    }

    const handleClick = () => {
        setTimeout(showMessage,3000);
    };

    return(
        <button onClick = {handleClick}>Follow</button>
    )
}

props의 user를 버튼을 누르고 3초뒤에 user의 이름을 띄워주는 컴포넌트입니다.

위 컴포넌트를 똑같이 클래스로도 만들 수 있습니다.

class Profile extends React.Component {
    showMessage = () => {
        alert(this.props.user);
    }

    handleClick = () => {
        setTimeout(this.showMessage,3000);
    }

    render(){
        return <button onClick = {this.handleClick}>Follow</button>
    }
}

얼핏보면 똑같다고 생각할 수 있지만, 이는 다르게 동작합니다.

이 곳에서 실제 확인할 수 있습니다.

먼저

결과를 보면 뭔가가 잘못된 것을 볼 수 있습니다.

함수형 컴포넌트에서는 이전에 선택되었던 프로필이 그대로 나오는 반면, 클래스형 컴포넌트는 3초가 지나기전에 바꾼 프로필이 알림으로 등장합니다.

마크다운 이미지

왜 이런 차이가 발생할까요?

클래스의 this.props를 통해 알림창을 먼저 띄우고 있습니다. props는 리액트에서 불변값입니다.

props는 오직 부모의 컴포넌트 안에서만 제어가능하고, 자식 컴포넌트에서는 이를 변경할 수 없습니다.

props

이런 점은 props가 불변하고 읽기 전용이다는 규칙을 제공해줍니다.

클래스 컴포넌트의 코드를 다시보면

class Profile extends React.Component {
    showMessage = () => {
        alert(this.props.user);
    }
}

props는 앞서 설명한대로 불변하고, 읽기 전용의 값입니다. 그러나 this는 변경 가능하고, 조작가능합니다

만약, 요청을 진행하고 , 클래스 컴포넌트가 다시 랜더링된다면, this의 props도 똑같이 바뀌게 될 것입니다. 결국 showMessage는 새로운 props의 user를 읽게 됩니다.

따라서 만약 setState등으로 상태가 변경된다면 react의 this.state나 this.props는 변경된 state를 참조하게 됩니다.

그럼 원래의 선택된 기존의 프로필, 원래의props를 유지하려면 어떻게 할까요?

다음의 코드로 수정이 가능합니다.

class Profile extends React.Component {
    showMessage = (user) => {
        alert(user);
    }

    handleClick = () => {
        const { user } = this.props
        setTimeout(this.showMessage(user),3000);
    }

    render(){
        return <button onClick = {this.handleClick}>Follow</button>
    }
}

현재 props에서 user를 뽑아오고 이를 setTimeout 함수로 전달합니다. 이렇게 하면 올바르게 setTimeout에 props를 전달할 수 있습니다.

다만, props가 복잡해지면 복잡해질수록, prop를 처리하는 함수가 많아지면 많아질수록, 이를 추적하는 것에 어려움을 겪을 수 있습니다.

함수형 컴포넌트

이번에는 함수형 컴포넌트로 다시 돌아가 보겠습니다. 클래스형 컴포넌트와 함수형 컴포넌트의 가장 다른 점은

함수형 컴포넌트는 render될떄의 그 값을 그대로 캡쳐해서 유지한다

입니다.

예를 들어

function MessageThread() {
  const [message, setMessage] = useState('');

  const showMessage = () => {
    alert('You said: ' + message);
  };

  const handleSendClick = () => {
    setTimeout(showMessage, 3000);
  };

  const handleMessageChange = (e) => {
    setMessage(e.target.value);
  };

  return (
    <>
      <input value={message} onChange={handleMessageChange} />
      <button onClick={handleSendClick}>Send</button>
    </>
  );
}

위의 클릭핸들러 함수는 호출되었을 당시의 state (message)를 기억해둡니다. 따라서 클래스컴포넌트의 동작과 다르게, 선택한 그 떄 당시의 상태를 알고 있습니다.

그러면 어떻게 이를 구현할 수 있을까요?

우리가 특정 시점의 상태를 그대로 따라가기 위해서는 자바스크립트의 클로저라는 것을 사용할 수 있습니다.

useState와 클로저

그럼 함수형 컴포넌트에서 최근 시점의 props,state를 받아오려면 어떻게 할까요? 클래스형 컴포넌트에서는 this.state,this.props등으로 가장 최근의 상태를 가져올 수 있었습니다.

함수형컴포넌트에서는 ref를 사용할 수 있습니다. 이를 이용해 최신의 상태를 동기화 할 수 있습니다.

function MyComponent() {
  const ref = useRef(null);
  // `ref.current`로 읽고 쓸 수 있다.
}

앞서 코드를 ref를 사용해 현재 상태를 갱신하게 설정하려면 useEffect훅을 사용할 수 있었다. props나 state가 바뀌면 이를 추적할 수 있습니다.

function MessageThread() {
  const [message, setMessage] = useState('');

  const latestMessage = useRef('');

  //최신의 message를 ref에 저장함
  useEffect(() => {
    latestMessage.current = message;
  },[message]);

  const showMessage = () => {
    alert('You said: ' + latestMessage.current);
  };

  const handleMessageChange = (e) => {
    setMessage(e.target.value);
  };
}

props나 state는 고정시키는 것이 좋지만, 이렇게 ref로 최신의 상태를 가져올 수도 있습니다.